package com.hsc.mvc.config;

import com.hsc.mvc.Configuration;
import com.hsc.mvc.tag.*;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author
 * @date 2025-05-18
 * @Description xml读取器的公共实现   不同的读取器是xml内容的读取来源不同
 * 类只能单继承 但是可以多实现
 * AbstractXMLReader 类的作用：
 * 不同的读取器是xml内容的读取来源不同
 * 一定读取到配置信息，对配置信息的处理，都是一致的。
 * 所以框架为xml读取器提供一个公共个父类，实现公共的读取处理
 * <p>
 * AbstractReader类的作用：
 * 所有的读取器在读取后都需要将配置信息存储在统一的configuration中
 * <p>
 * ConfigReader接口类的作用：
 * 配置读取类 我们使用接口来定义统一的标准 用于管理下面实现的类 都有读取的配置文件的方法
 */
public abstract class AbstractXMLReader extends AbstractReader implements ConfigReader {

    /**
     * 必须重写AbstractXMLReader构造方法 对AbstractReader 构造器进行调用
     * 然后存储 configuration
     *
     * @param configuration
     */
    public AbstractXMLReader(Configuration configuration) {
        super(configuration);
    }

    /**
     * 不同的读取器是xml内容的读取来源不同
     * 一定读取到配置信息，对配置信息的处理，都是一致的。
     * 所以框架为xml读取器提供一个公共个父类，实现公共的读取处理
     * 不同实现类会进行调用
     *
     * @param inputStream 直接传入一个输入流
     */
    protected void read(InputStream inputStream) {
        // 引入一个外部的jar 工具  用来读取配置文件的
        try {
            SAXReader saxReader = new SAXReader();
            // 操作document 对象读取我们需要的信息
            Document document = saxReader.read(inputStream);
            // 开始读取标签
            // 读取 mapper 标签 <mapper> controller 的映射
            readMapping(document);
            // 读取<aop-mapper> 标签Interceptor 拦截器切面映射
            readAopMapping(document);
            // 读取参数绑定器标签
            readParamBinder(document);
            // 读取类型转换器信息
            readConvertor(document);

        } catch (DocumentException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 读取类型转换器标签
     * @param document
     */
    private void readConvertor(Document document) {
        List<Element> elementList = document.selectNodes("mvc/convertor");
        for (Element element : elementList) {
            String convertorClass = element.attributeValue("class");
            ConvertorTag convertorTag = new ConvertorTag();
            convertorTag.setClassStr(convertorClass);
            configuration.addConvertor(convertorTag);

        }
    }

    /**
     * 读取参数绑定器相关标签
     *
     * @param document
     */
    private void readParamBinder(Document document) {
        // 获取标签
        List<Element> elementList = document.selectNodes("mvc/param-binder");
        for (Element element : elementList) {
            // 获取参数绑定器的class
            String classStr = element.attributeValue("class");
            // 参数绑定器标签对象对象信息
            ParamBinderTag paramBinderTag = new ParamBinderTag();
            paramBinderTag.setClassStr(classStr);
            configuration.addBinder(paramBinderTag);
        }
    }

    /**
     * 读取mapping标签 ： 目标映射
     * <mapper name="/test1.do" class="com.controller.TestController" method="t1">
     * <param-type name="age" type="cookie">int</param-type>
     * <param-type name="sex" type="header">java.lang.String</param-type>
     * </mapper>
     *
     * @param document
     */
    private void readMapping(Document document) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 获取所有的mapper标签
        // <mvc>
        //    <!--配置请求与处理器的映射 让mvc框架可以根据信息进行反射创建-->
        //    <mapper name="/test1.do" class="com.controller.TestController" method="t1">
        List<Element> elementList = document.selectNodes("mvc/mapper");
        // 遍历 每一个 <mapper> 封装成一个对象
        for (Element mappingTag : elementList) {
            // 得到所有的mapper 标签信息
            String name = mappingTag.attributeValue("name");
            String classPath = mappingTag.attributeValue("class");
            String method = mappingTag.attributeValue("method");

            // 将信息封装到mapping对象中
            MappingTag mapping = new MappingTag();
            mapping.setNameStr(name);
            // 设置Class 字符串的同时 设置Class对象
            mapping.setClassStr(classPath);
            // 设置方法名 同时根据方法名和参数类型设置Method对象
            mapping.setMethodStr(method);
            // 获取当前mapper标签下的param-type标签
            List<Element> paramTypeList = mappingTag.selectNodes("param-type");
            List<ParamTypeTag> paramTypeTagList = new ArrayList<>();
            for (Element paramTypeTag : paramTypeList) {
                //     <param-type name="age" type="cookie">int</param-type>
                String nameStr = paramTypeTag.attributeValue("name");
                String typeStr = paramTypeTag.attributeValue("type");
                String text = paramTypeTag.getText();
                ParamTypeTag paramTypeTag1 = new ParamTypeTag(nameStr, typeStr, text);
                paramTypeTagList.add(paramTypeTag1);
            }
            mapping.setParamTypeTagList(paramTypeTagList);
            // 一个完整的 mapping对象封装完成，放入到容器中
            // 所有的读取器在读取后都需要将配置信息存储在统一的configuration中
            // 我们再创建一个公共的抽象类AbstractReader 里面放入一个configuration对象 用来存储配置信息
            // 还需要将其缓存在configuration中 key 为 name="/test1.do"
            configuration.putMapping(mapping);

            // 这里直接对controller 的对象进行保存 使用单实例模式
            Class<?> key = mapping.getMappingClass();
            if (!configuration.isExist(key)) {
                Object singleObject = key.getConstructor().newInstance();
                configuration.putSingleObject(key, singleObject);
            }

        }
    }

    /**
     * <aop-mapper class="com.interceptor.MyInterceptor3"
     * include="/*"
     * exclude="/test1.do"/>
     *
     * @param document
     */
    private void readAopMapping(Document document) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 每一个标签会有三个属性  class  include  exclude
        List<Element> elementList = document.selectNodes("mvc/aop-mapper");
        // 解析保存到对象中
        for (Element element : elementList) {
            String classStr = element.attributeValue("class");
            String include = element.attributeValue("include");
            String exclude = element.attributeValue("exclude");
            AopMappingTag aopTag = new AopMappingTag();
            // 设置类的字符串的同时 设置对应的Class对象
            aopTag.setClassStr(classStr);
            aopTag.setInclude(include);
            aopTag.setExclude(exclude);
            // 将配置保存到configuration中
            // configuration 父类继承中来
            configuration.putAopMapping(aopTag);
            // 将对象实体化 并且保存
            Class<?> aopClass = aopTag.getAopClass();
            if (!configuration.isExist(aopClass)) {
                Object singleObject = aopClass.getConstructor().newInstance();
                configuration.putSingleObject(aopClass, singleObject);
            }
        }


    }
}
